package com.novax.ex.activity.provider.service;

import com.novax.ex.activity.infrastructure.entity.IeoConfig;
import com.novax.ex.activity.infrastructure.entity.IeoCurrencyConfig;
import com.novax.ex.activity.infrastructure.entity.IeoDistribution;
import com.novax.ex.activity.infrastructure.entity.IeoOperationErrorLog;
import com.novax.ex.activity.infrastructure.entity.IeoOrder;
import com.novax.ex.activity.infrastructure.entity.IeoStages;
import com.novax.ex.activity.infrastructure.entity.IeoVipLimit;
import com.novax.ex.activity.infrastructure.mapper.IeoConfigMapper;
import com.novax.ex.activity.infrastructure.mapper.IeoCurrencyConfigMapper;
import com.novax.ex.activity.infrastructure.mapper.IeoDistributionMapper;
import com.novax.ex.activity.infrastructure.mapper.IeoOperationErrorLogMapper;
import com.novax.ex.activity.infrastructure.mapper.IeoOrderMapper;
import com.novax.ex.activity.infrastructure.mapper.IeoStagesMapper;
import com.novax.ex.activity.infrastructure.mapper.IeoVipLimitMapper;
import com.novax.ex.activity.open.model.request.IeoOrderRequest;
import com.novax.ex.activity.open.model.request.IeoOrderUpdateRequest;
import com.novax.ex.activity.open.model.request.IeoRecommendRequest;
import com.novax.ex.activity.open.model.response.IeoBuyRecommendResponse;
import com.novax.ex.activity.open.model.response.IeoSubscribeCopyResponse;
import com.novax.ex.activity.provider.api.ActivityInfoApi;
import com.novax.ex.activity.provider.api.InternationalApi;
import com.novax.ex.activity.provider.api.UserAssetApi;
import com.novax.ex.activity.provider.api.model.AssetChangeRequest;
import com.novax.ex.activity.provider.api.model.UserActivityInfo;
import com.novax.ex.activity.provider.api.model.UserAsset;
import com.novax.ex.activity.provider.common.Operation;
import com.novax.ex.common.base.BaseMapper;
import com.novax.ex.common.base.BaseService;
import com.novax.ex.common.constant.ActivityConstants;
import com.novax.ex.common.constant.Currency;
import com.novax.ex.common.constant.InternationalCode;
import com.novax.ex.common.constant.StatusCode;
import com.novax.ex.common.core.redis.RedisUtil;
import com.novax.ex.common.enums.IeoOrderStatus;
import com.novax.ex.common.results.ReturnResult;
import com.novax.ex.common.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import jakarta.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @Description 申购订单service
 * @Author Hsylient
 * @Date 2022/12/05/13:54
 */
@Slf4j
@Service
public class IeoOrderService extends BaseService {

    private static final String COPIES_REMAINING = "copiesRemaining";

    private static final String WEIGHT_REMAINING = "weightRemaining";

    @Resource
    private InternationalApi internationalApi;

    @Resource
    private IeoOrderMapper ieoOrderMapper;

    @Resource
    private IeoVipLimitMapper ieoVipLimitMapper;

    @Resource
    private ActivityInfoApi activityInfoApi;

    @Resource
    private IeoCurrencyConfigMapper ieoCurrencyConfigMapper;

    @Resource
    private UserAssetApi userAssetApi;

    @Resource
    private IeoConfigMapper ieoConfigMapper;

    @Resource
    private IeoDistributionMapper ieoDistributionMapper;

    @Resource
    private IeoStagesMapper ieoStagesMapper;

    @Resource
    private IeoOperationErrorLogMapper ieoOperationErrorLogMapper;

    @Resource
    private ExecutorService executorService;

    @Override
    protected BaseMapper getBaseMapper() {
        return ieoOrderMapper;
    }

    /**
     * @Description: 申购订单生成
     * @Author: ChenXi
     * @Date: 2022/12/7 11:41
     * @Param: [userId, language, orderRequest]
     * @return: com.novax.ex.common.results.ReturnResult<?>
     **/
    @Transactional(rollbackFor = Exception.class)
    public ReturnResult<?> addOrder(Long userId, String language, String uri, IeoOrderRequest orderRequest) {
        // 认购份数
        Integer amount = orderRequest.getAmount();
        if (Objects.isNull(amount)) {
            log.info("[IEO order add] amount:{}", amount);
            return ReturnResult.fail(getInternational(language, InternationalCode.LACK_VALUE));
        }

        if(amount <= 0){
            log.info("[IEO order add] amount:{}", amount);
            return ReturnResult.fail(getInternational(language, InternationalCode.BUY_COPIES_IS_ZERO));
        }
        ReturnResult<UserActivityInfo> info = activityInfoApi.findInfo(userId);
        UserActivityInfo userActivityInfo = info.getData();
        // 获取到用户的注册时间
        Date registerTime = userActivityInfo.getRegisterTime();
        if (Objects.isNull(registerTime)) {
            log.info("[IEO order add] registerTime is null");
            return ReturnResult.fail(getInternational(language, InternationalCode.NOT_FOUND));
        }
        IeoConfig ieoConfig = ieoConfigMapper.selectByPrimaryKey(orderRequest.getIeoId());
        if (Objects.isNull(ieoConfig)) {
            log.info("[IEO order add] ieoConfig is null");
            return ReturnResult.fail(getInternational(language, InternationalCode.NOT_FOUND));
        }

//        // 拿到用户的注册时间
        Long differenceDays = DateUtil.intervalTime(new Date(), registerTime);
        if (differenceDays < ieoConfig.getRegister()) {
            return ReturnResult.result(null, 506, getInternational(language, InternationalCode.NO_QUALIFICATION));
        }

        Date startTime = ieoConfig.getStartTime();
        Date endTime = ieoConfig.getEndTime();
        Integer state = ieoConfig.getState();
        Date now = new Date();
        // 未在活动时间内或者活动未开启
        if (now.before(startTime) || now.after(endTime) || state != 1) {
            log.info("[IEO order add] now before startTime or now after endTime or state = 0");
            return ReturnResult.fail(getInternational(language, InternationalCode.ACTIVE_TIME_OR_STATUS));
        }

        // 判断vip等级
        Integer vipLevel = ieoConfig.getVipLimit();
        Integer userVipLevel = userActivityInfo.getVipLevel();
        if (userVipLevel < vipLevel) {
            log.info("[IEO order add] userVipLevel low,userVipLevel:{}, vipLevel:{}", userVipLevel, vipLevel);
            return ReturnResult.fail(getInternational(language, InternationalCode.MINIMUM_VIP_LEVEL));
        }

        //防止表单重复提交
        String repeatKey = repeatForm(uri, userId);
        if (!StringUtils.hasText(repeatKey)) {
            return new ReturnResult(StatusCode.ERROR, getInternational(language, InternationalCode.RESUBMIT));
        }

        // 获取该用户购买过的份数
        Integer totalNumberCopies = ieoOrderMapper.selectCopiesSumByUserIdAndIeoId(userId, orderRequest.getIeoId());
        if (Objects.isNull(totalNumberCopies)) {
            totalNumberCopies = 0;
        }
        // 此次请求购买的份数
        Integer requestAmount = orderRequest.getAmount();
        // 通过用户vip等级和id查询到限购份数
        IeoVipLimit ieoVipLimit =
                ieoVipLimitMapper.selectByIeoIdAndVipLevel(orderRequest.getIeoId(), userVipLevel);
        int limit = 0;
        if (Objects.nonNull(ieoVipLimit)) {
            limit = ieoVipLimit.getLimit();
        }

        // 超出购买限制 返回剩余可购买份数
        if (totalNumberCopies + requestAmount > limit) {
            String international = getInternational(language, InternationalCode.UP_SUBSCRIPTION);
            String replace = international.replace("{0}",
                    Integer.toString(limit - totalNumberCopies));
            //删除redis标记
            RedisUtil.delete(repeatKey);
            return ReturnResult.fail(replace);
        }

        // 查询当前支付币种购买IEO打新币所需每份数量
        Long ieoId = orderRequest.getIeoId();
        // 支付币种
        String payCurrency = orderRequest.getPayCurrency();
        // 查询申购币种配置
        IeoCurrencyConfig ieoCurrencyConfig = ieoCurrencyConfigMapper.selectByIeoIdAndCurrency(ieoId, payCurrency);
        if (Objects.isNull(ieoCurrencyConfig)) {
            log.info("[IEO order add] ieoCurrencyConfig is null");
            return ReturnResult.fail(getInternational(language, InternationalCode.OPERATION_ERROR));
        }

        // 支付总额
        BigDecimal totalExpenditure = this.getTotalPaymentQuantity(orderRequest, ieoCurrencyConfig);

        // 是否余额不足
        if (this.determineUserBalanceSufficePay(userId, payCurrency, totalExpenditure)) {
            log.info("[IEO order add] 余额不足 is null");
            //删除redis标记
            RedisUtil.delete(repeatKey);
            return ReturnResult.result(null, 505, getInternational(language, InternationalCode.CREDIT_RUN_LOW));
        }

        // 认证成功 生成订单 返回订单id
        Long orderId = this.orderGeneration(userId, orderRequest, totalExpenditure, userActivityInfo, ieoCurrencyConfig, ieoConfig);

        // 资金冻结
        ReturnResult<?> returnResult = ieoAssetOfChange(userId, orderId, orderRequest.getPayCurrency(), totalExpenditure, Operation.IEO_ORDER);

        //删除redis标记
        RedisUtil.delete(repeatKey);

        if(Objects.nonNull(returnResult) && returnResult.isSuccess()){
            return ReturnResult.success(getInternational(language, InternationalCode.ORDER_SUCCESS));
        } else {
            return ReturnResult.fail(getInternational(language, InternationalCode.OPERATION_ERROR));
        }

    }

    /**
     * @Description: 资金操作错误日志生成
     * @Author: ChenXi
     * @Date:  2023/7/7 15:48
     * @param userId
     * @param orderId
     * @param currency
     * @param totalExpenditure
     * @param operation
     * @return: void
     **/
    private void ieoOperationErrorLogBuild(Long userId, Long orderId, String currency, BigDecimal totalExpenditure, Integer operation) {
        IeoOperationErrorLog ieoOperationErrorLog = new IeoOperationErrorLog();
        ieoOperationErrorLog.setUserId(userId);
        ieoOperationErrorLog.setOrderId(orderId);
        ieoOperationErrorLog.setCurrency(currency);
        ieoOperationErrorLog.setAmount(totalExpenditure);
        ieoOperationErrorLog.setOperation(operation);
        ieoOperationErrorLogMapper.insertSelective(ieoOperationErrorLog);
    }

    /**
     * 防止重复提交 uid
     *
     * @param uri uri
     * @param userId  唯一id
     * @return string
     */
    public String repeatForm(String uri, Long userId) {
        String repeatKey = ActivityConstants.redisType.REDIS_KEY_REPEAT + userId + uri;
        if (RedisUtil.get(repeatKey) != null) {
            return "";
        } else {
            RedisUtil.set(repeatKey, "1", 15L, TimeUnit.SECONDS);
            return repeatKey;
        }
    }

    /**
     * @Description: 购买推荐
     * @Author: ChenXi
     * @Date:  2022/12/30 11:50
     * @Param: [userId, language, orderRequest]
     * @return: com.novax.ex.common.results.ReturnResult<?>
     **/
    public ReturnResult<IeoBuyRecommendResponse> recommend(Long userId, String language, IeoRecommendRequest ieoRecommendRequest) {
        Long ieoId = ieoRecommendRequest.getIeoId();
        if(Objects.isNull(ieoId)){
            return ReturnResult.fail(getInternational(language, InternationalCode.NOT_FOUND));
        }

        // 查询用户信息
        ReturnResult<UserActivityInfo> info = activityInfoApi.findInfo(userId);
        if(Objects.isNull(info) || Objects.isNull(info.getData())){
            return ReturnResult.fail(getInternational(language, InternationalCode.NOT_FOUND));
        }
        UserActivityInfo userActivityInfo = info.getData();

        // 查询ieo活动配置
        IeoConfig ieoConfig = ieoConfigMapper.selectByPrimaryKey(ieoId);
        if(Objects.isNull(ieoConfig)){
            return ReturnResult.fail(getInternational(language, InternationalCode.NOT_FOUND));
        }

        // 判断vip等级
        Integer vipLevel = ieoConfig.getVipLimit();
        Integer userVipLevel = userActivityInfo.getVipLevel();
        if (userVipLevel < vipLevel) {
            log.info("[IEO order add] userVipLevel low,userVipLevel:{}, vipLevel:{}", userVipLevel, vipLevel);
            return ReturnResult.fail(getInternational(language, InternationalCode.MINIMUM_VIP_LEVEL));
        }

        // 获取该用户购买过的份数
        Integer totalNumberCopies = ieoOrderMapper.selectCopiesSumByUserIdAndIeoId(userId, ieoId);
        if (Objects.isNull(totalNumberCopies)) {
            totalNumberCopies = 0;
        }

        // 用户vip等级限购数
        IeoVipLimit ieoVipLimit = ieoVipLimitMapper.selectByIeoIdAndVipLevel(ieoId, userVipLevel);
        Integer vipLimit = Objects.nonNull(ieoVipLimit) ? ieoVipLimit.getLimit() : 0 ;

        // 用户剩余可购买数
        Integer remainingLimit  = vipLimit - totalNumberCopies;

        if(remainingLimit <= 0){
            return ReturnResult.fail(getInternational(language, InternationalCode.EXCEED_LIMIT));
        }

        // 用户剩余可购买数 (BigDecimal)
        BigDecimal remainingLimit2BigDecimal = new BigDecimal(remainingLimit);

        // et币种配置
        IeoCurrencyConfig etCurrencyConfig = ieoCurrencyConfigMapper.selectByIeoIdAndCurrency(ieoId, "et");
        // 查询用户et余额能购买的份数
        BigDecimal enoughCopiesToBuyByEt = getCopiesByCurrency(userId, etCurrencyConfig);

        // 推荐购买response (随着遍历计算出更高的可购买数会改变)
        IeoBuyRecommendResponse ieoBuyRecommendResponse = new IeoBuyRecommendResponse();
        ieoBuyRecommendResponse.setAmount(BigDecimal.ZERO);

        // et高优先级 如果et足够购买剩余的所有份数则直接返回et
        if(enoughCopiesToBuyByEt.compareTo(remainingLimit2BigDecimal) >= 0 ){
            return ReturnResult.success(ieoBuyRecommendResponseBuild("et", remainingLimit2BigDecimal));
        } else {
            // 获取所有币种配置
            List<IeoCurrencyConfig> ieoCurrencyConfigs = ieoCurrencyConfigMapper.selectByIeoId(ieoId);
            // 遍历所有币种配置并计算用户最高购买数
            for (IeoCurrencyConfig ieoCurrencyConfig : ieoCurrencyConfigs) {
                BigDecimal enoughCopiesToBuy;
                // et跳过 (已计算过)
                if("et".equals(ieoCurrencyConfig.getCurrency())){
                    enoughCopiesToBuy = enoughCopiesToBuyByEt;
                } else {
                    enoughCopiesToBuy = getCopiesByCurrency(userId, ieoCurrencyConfig);
                }

                // 可购买份数 = 0份跳过
                if(BigDecimal.ZERO.equals(enoughCopiesToBuy)){
                    continue;
                } else {
                    // 用户余额可购买份数 > 限购数  直接取值返回
                    if(enoughCopiesToBuy.compareTo(remainingLimit2BigDecimal) >= 0){
                        ieoBuyRecommendResponse.setCurrency(ieoCurrencyConfig.getCurrency());
                        ieoBuyRecommendResponse.setAmount(remainingLimit2BigDecimal);
                        break;
                    } else {
                        // 用户余额可购买份数 < 限购数
                        if(enoughCopiesToBuy.compareTo(ieoBuyRecommendResponse.getAmount()) > 0){
                            ieoBuyRecommendResponse.setCurrency(ieoCurrencyConfig.getCurrency());
                            ieoBuyRecommendResponse.setAmount(enoughCopiesToBuy);
                        } else if(enoughCopiesToBuy.compareTo(ieoBuyRecommendResponse.getAmount()) == 0){
                            // 如果相同判断是否有et 有et优先et
                            if("et".equals(ieoCurrencyConfig.getCurrency()) || "et".equals(ieoBuyRecommendResponse.getCurrency())){
                                ieoBuyRecommendResponse.setCurrency("et");
                            }
                        }
                    }
                }
            }
        }

        // 如果为空 给默认币种为计价币种
        if (!StringUtils.hasText(ieoBuyRecommendResponse.getCurrency())){
            IeoCurrencyConfig byIsPricingCurrency = ieoCurrencyConfigMapper.findByIsPricingCurrency(ieoId);
            ieoBuyRecommendResponse.setCurrency(byIsPricingCurrency.getCurrency());
        }
        return ReturnResult.success(ieoBuyRecommendResponse);
    }


    /**
     * @Description: IeoBuyRecommendResponse build
     * @Author: ChenXi
     * @Date:  2022/12/30 14:16
     * @Param: [remainingLimit2BigDecimal, etCurrencyConfig]
     * @return: com.novax.ex.activity.open.model.response.IeoBuyRecommendResponse
     **/
    private IeoBuyRecommendResponse ieoBuyRecommendResponseBuild(String currency, BigDecimal payAmount) {
        IeoBuyRecommendResponse ieoBuyRecommendResponse = new IeoBuyRecommendResponse();
        ieoBuyRecommendResponse.setCurrency(currency);
        ieoBuyRecommendResponse.setAmount(payAmount);
        return ieoBuyRecommendResponse;
    }

    /**
     * @Description:  根据币种配置计算可购买的份数(IEO 5期)
     * @Author: ChenXi
     * @Date:  2023/3/10 17:37
     * @param userId 用户id
     * @param ieoCurrencyConfig 币种配置
     **/
    public Map<String, BigDecimal> getCopiesByCurrency(IeoCurrencyConfig ieoCurrencyConfig, Long userId) {
        Map<String, BigDecimal> map = new HashMap<>();
        if(Objects.isNull(ieoCurrencyConfig)){
            return map;
        }

        // 查询用户et余额
        ReturnResult<UserAsset> asset = userAssetApi.findAsset(userId,ieoCurrencyConfig.getCurrency());
        if(Objects.isNull(asset) || Objects.isNull(asset.getData()) ){
            return map;
        }

        BigDecimal available = asset.getData().getAvailable();
        // 用户余额
        map.put("userAmount", available);
        // 计算余额能够购买的份数
        map.put("copiesByCurrency", available.divide(ieoCurrencyConfig.getAmount(), 0, RoundingMode.DOWN));
        return map;
    }

    /**
     * @Description:  根据币种配置计算可购买的份数
     * @Author: ChenXi
     * @Date:  2023/3/10 17:37
     * @param userId 用户id
     * @param ieoCurrencyConfig 币种配置
     **/
    private BigDecimal getCopiesByCurrency(Long userId, IeoCurrencyConfig ieoCurrencyConfig) {
        if(Objects.isNull(ieoCurrencyConfig)){
            return BigDecimal.ZERO;
        }

        // 查询用户et余额
        ReturnResult<UserAsset> asset = userAssetApi.findAsset(userId,ieoCurrencyConfig.getCurrency());
        if(Objects.isNull(asset) || Objects.isNull(asset.getData()) ){
            return BigDecimal.ZERO;
        }

        // 计算余额能够购买的份数
        return asset.getData().getAvailable()
                .divide(ieoCurrencyConfig.getAmount(), 0, RoundingMode.DOWN);
    }

    /**
     * @Description: 获取总支付数量
     * @Author: Cody
     * @Date:  2022/12/6 10:41
     * @Param: [userId, orderRequest]
     * @return: java.math.BigDecimal
     **/
    private BigDecimal getTotalPaymentQuantity(IeoOrderRequest orderRequest, IeoCurrencyConfig ieoCurrencyConfig) {
        // 购买份数
        BigDecimal amount = new BigDecimal(orderRequest.getAmount());
        BigDecimal totalExpenditure = ieoCurrencyConfig.getAmount().multiply(amount);
        return totalExpenditure;
    }

    /**
     * @Description: 申购订单生成
     * @Author: Cody
     * @Date:  2022/12/6 11:52
     * @Param: [userId, orderRequest, totalExpenditure]
     * @return: boolean
     **/
    private Long orderGeneration(Long userId,
                                 IeoOrderRequest orderRequest,
                                 BigDecimal totalExpenditure,
                                 UserActivityInfo userActivityInfo,
                                 IeoCurrencyConfig ieoCurrencyConfig,
                                 IeoConfig ieoConfig) {
        IeoOrder ieoOrder = new IeoOrder();
        // 活动id
        ieoOrder.setIeoId(orderRequest.getIeoId());
        // 用户id
        ieoOrder.setUserId(userId);
        // 活动名称
        ieoOrder.setProjectName(ieoConfig.getProjectName());
        // 申购币种
        ieoOrder.setCurrency(ieoConfig.getCurrency());
        // 币种权重
        ieoOrder.setWeight(this.weightCalculi(userActivityInfo, ieoCurrencyConfig, ieoConfig));
        // 份数
        ieoOrder.setCopies(orderRequest.getAmount());
        // 支付币种
        ieoOrder.setPayCurrency(orderRequest.getPayCurrency());
        // 支付数量
        ieoOrder.setPayAmount(totalExpenditure);
        // 实际支出数量 活动结束之后再计算 先给默认值0
        ieoOrder.setRealPayAmount(BigDecimal.ZERO);
        // 创建时间
        ieoOrder.setCreateTime(new Date());
        // 返回自增主键
        ieoOrderMapper.insertSelective(ieoOrder);
        return ieoOrder.getId();
    }

    /**
     * @Description:  计算权重
     * @Author: Cody
     * @Date:  2022/12/6 14:17
     * @Param: [ieoOrderRequest, userMap]
     * @return: java.math.BigDecimal
     **/
    private BigDecimal weightCalculi(UserActivityInfo userActivityInfo, IeoCurrencyConfig ieoCurrencyConfig, IeoConfig ieoConfig) {
        // 用户邀请人数
        Integer userInviteeNumber = userActivityInfo.getInviteeNumber();
        // 有效下级用户的门槛数量
        Integer invitee = ieoConfig.getInvitee();
        // 币种权重
        BigDecimal weight = ieoCurrencyConfig.getWeight();
        BigDecimal finalWeight;
        if(userInviteeNumber >= invitee){
            // 达到邀请人数门槛 最终权重 = 币种权重 * 邀请权重
            finalWeight = weight.multiply(ieoConfig.getCoefficient().divide(new BigDecimal(100),8,BigDecimal.ROUND_DOWN).add(new BigDecimal(1)));
        } else {
            // 达到邀请人数门槛 权重 = 币种权重
            finalWeight = weight;
        }

        return finalWeight;
    }

    /**
     * @Description: 计算用户是否支付币种余额不足
     * @Author: Cody
     * @Date:  2022/12/6 14:47
     * @Param: [userId, PayCurrency, totalExpenditure]
     * @return: boolean
     **/
    private boolean determineUserBalanceSufficePay(Long userId, String payCurrency, BigDecimal totalExpenditure) {
        // 查询用户币种余额
        ReturnResult<UserAsset> asset = userAssetApi.findAsset(userId,payCurrency);
        if(Objects.isNull(asset)){
            log.info("[IEO order unfree] 查询资产失败, userId:{}", userId);
            return false;
        }
        BigDecimal available = asset.getData().getAvailable();
        // 是否余额不足
        return totalExpenditure.compareTo(available) > 0;
    }

    /**
     * @Description: IEO资金操作 重载 盈亏分析迭代版(在发币和分期发币时带入了价格)
     * @Author: ChenXi
     * @Date:  2022/12/9 13:36
     * @Param: [userId, orderId, payCurrency, totalExpenditure, operation]
     * @return: com.novax.ex.common.results.ReturnResult
     **/
    public ReturnResult<?> ieoAssetOfChange(Long userId, Long orderId, String payCurrency, BigDecimal total, Integer operation, BigDecimal price) {
        // IEO操作
        try {
            ReturnResult returnResult = assetOfChange(userId, orderId, payCurrency, total, operation, price);

            // 订单生成默认状态为无效, 扣款成功后更改为正常
            if(operation.equals(Operation.IEO_ORDER) && Objects.nonNull(returnResult) && returnResult.isSuccess()){
                IeoOrder ieoOrder = new IeoOrder();
                ieoOrder.setId(orderId);
                ieoOrder.setStatus(IeoOrderStatus.normal.getType());
                ieoOrderMapper.updateByPrimaryKeySelective(ieoOrder);
            }

            if(Objects.isNull(returnResult) || !returnResult.isSuccess()){
                // IEO资金操作错误日志生成
                ieoOperationErrorLogBuild(userId, orderId, payCurrency, total, operation);
                return ReturnResult.fail("资产操作失败");
            }

        } catch (Exception e) {
            // IEO资金操作错误日志生成
            ieoOperationErrorLogBuild(userId, orderId, payCurrency, total, operation);
            return ReturnResult.fail("资产操作失败");
        }
        return ReturnResult.success();
    }

    /**
     * @Description: IEO资金操作
     * @Author: ChenXi
     * @Date:  2022/12/9 13:36
     * @Param: [userId, orderId, payCurrency, totalExpenditure, operation]
     * @return: com.novax.ex.common.results.ReturnResult
     **/
    public ReturnResult<?> ieoAssetOfChange(Long userId, Long orderId, String payCurrency, BigDecimal total, Integer operation) {
        // IEO操作
        try {
            ReturnResult returnResult = assetOfChange(userId, orderId, payCurrency, total, operation);

            // 订单生成默认状态为无效, 扣款成功后更改为正常
            if(operation.equals(Operation.IEO_ORDER) && Objects.nonNull(returnResult) && returnResult.isSuccess()){
                IeoOrder ieoOrder = new IeoOrder();
                ieoOrder.setId(orderId);
                ieoOrder.setStatus(IeoOrderStatus.normal.getType());
                ieoOrderMapper.updateByPrimaryKeySelective(ieoOrder);
            }

            if(Objects.isNull(returnResult) || !returnResult.isSuccess()){
                // IEO资金操作错误日志生成
                ieoOperationErrorLogBuild(userId, orderId, payCurrency, total, operation);
                return ReturnResult.fail("资产操作失败");
            }

        } catch (Exception e) {
            // IEO资金操作错误日志生成
            ieoOperationErrorLogBuild(userId, orderId, payCurrency, total, operation);
            return ReturnResult.fail("资产操作失败");
        }
        return ReturnResult.success();
    }

    /**
     * @Description: 资金操作
     * @Author: ChenXi
     * @Date:  2022/12/9 13:36
     * @Param: [userId, orderId, payCurrency, totalExpenditure, operation]
     * @return: com.novax.ex.common.results.ReturnResult
     **/
    public ReturnResult assetOfChange(Long userId, Long orderId, String payCurrency, BigDecimal total, Integer operation) {
        AssetChangeRequest assetChangeRequest = new AssetChangeRequest();
        // 用户id
        assetChangeRequest.setUserId(userId);
        // 订单id
        assetChangeRequest.setOrderId(orderId);
        // 支付币种
        assetChangeRequest.setCurrency(payCurrency);
        // 标记
        assetChangeRequest.setSymbol(payCurrency);
        // 总支付金额
        assetChangeRequest.setAmount(total);
        // 行为 75=下单冻结 76=解冻 77=冻结支付 78=发放
        assetChangeRequest.setOperation(operation);
        return userAssetApi.assetChange(assetChangeRequest);
    }


    /**
     * @Description: 资金操作 重载 盈亏分析迭代版(在发币和分期发币时带入了价格)
     * @Author: ChenXi
     * @Date:  2022/12/9 13:36
     * @Param: [userId, orderId, payCurrency, totalExpenditure, operation]
     * @return: com.novax.ex.common.results.ReturnResult
     **/
    public ReturnResult assetOfChange(Long userId, Long orderId, String payCurrency, BigDecimal total, Integer operation, BigDecimal price) {
        AssetChangeRequest assetChangeRequest = new AssetChangeRequest();
        // 用户id
        assetChangeRequest.setUserId(userId);
        // 订单id
        assetChangeRequest.setOrderId(orderId);
        // 支付币种
        assetChangeRequest.setCurrency(payCurrency);
        // 标记
        assetChangeRequest.setSymbol(payCurrency);
        // 总支付金额
        assetChangeRequest.setAmount(total);
        // 行为 75=下单冻结 76=解冻 77=冻结支付 78=发放
        assetChangeRequest.setOperation(operation);
        // 在发币操作时携带价格
        assetChangeRequest.setPrice(price);
        return userAssetApi.assetChange(assetChangeRequest);
    }

    /**
     * 获取国际化翻译
     *
     * @param language language
     * @param code     国际化编码
     * @return string 国际化
     */
    protected String getInternational(String language, String code) {
        return internationalApi.getInternational(code, language).getData();
    }

    /**
     * @Description: 根据活动状态决定是否扣除冻结余额, 返还差价资金, 发放IEO打新币
     * @Author: ChenXi
     * @Date:  2023/3/24 09:36
     * @param ieoOrder  订单
     * @param remainingMap  map 存放总权重, 总分配份数 (递减)
     * @param amount  每份数量
     * @return: void
     **/
    private void orderProcessing(IeoOrder ieoOrder, Map<String, BigDecimal> remainingMap, BigDecimal amount) {
        // 剩余份数
        BigDecimal copiesOfRemaining = remainingMap.get(COPIES_REMAINING);
        // 剩余权重
        BigDecimal weightOfRemaining = remainingMap.get(WEIGHT_REMAINING);
        // 我购买的份数
        BigDecimal purchasedCopies = new BigDecimal(ieoOrder.getCopies());
        // 币种权重 * 邀请权重(达到了邀请人数门槛则会有此加成) * 购买份数
        BigDecimal orderWeight = ieoOrder.getWeight().multiply(purchasedCopies);
        // 根据权重计算理论上获取的份数 订单权重 / 剩余未分配用户总权重 * 剩余份数
        BigDecimal getCopiesByWeight = orderWeight
                .multiply(copiesOfRemaining)
                .divide(weightOfRemaining, 8, BigDecimal.ROUND_DOWN);

        // 计算差值 用户计算加权重之后的份数超过了购买份数则修正为购买份数
        BigDecimal differValue = getCopiesByWeight.subtract(purchasedCopies);
        // 此订单实际发币量
        BigDecimal actualQuantityDistribute;
        // 超出购买份数情况下 修正为购买份数
        if (differValue.compareTo(BigDecimal.ZERO) > 0) {
            actualQuantityDistribute = purchasedCopies.multiply(amount);
//            // 扣除冻结资金 发币
//            this.deductFrozenAndFundIssueCoin(ieoOrder, userId, actualQuantityDistribute);
            // 扣除已发放的份数 和 已发币订单的权重 只在超了的情况下进行递减, 未超之后按当时的(已扣除完超了的订单分完的份数以及权重) 获得份数 = 个人权重/总权重 * 份数
            remainingMap.put(COPIES_REMAINING, copiesOfRemaining.subtract(purchasedCopies));
            remainingMap.put(WEIGHT_REMAINING, weightOfRemaining.subtract(orderWeight));
            // 订单处理完更新订单状态
            this.ieoOrderUpdate(ieoOrder, actualQuantityDistribute, ieoOrder.getPayAmount(), 1);
        } else if (differValue.compareTo(BigDecimal.ZERO) == 0) {
            // 相等情况下
            actualQuantityDistribute = purchasedCopies.multiply(amount);
//            this.deductFrozenAndFundIssueCoin(ieoOrder, userId, actualQuantityDistribute);

            // 订单处理完更新订单状态
            this.ieoOrderUpdate(ieoOrder, actualQuantityDistribute, ieoOrder.getPayAmount(), 1);
        } else if (differValue.compareTo(BigDecimal.ZERO) < 0) {
            // 未超出购买份数情况下
            IeoCurrencyConfig ieoCurrencyConfig = ieoCurrencyConfigMapper.selectByIeoIdAndCurrency(ieoOrder.getIeoId(), ieoOrder.getPayCurrency());
            // 当前订单支付币种购买一份打新币的汇率
            BigDecimal exchangeRateAmount = ieoCurrencyConfig.getAmount();
            // 需要支付的数量
            BigDecimal payAmount = getCopiesByWeight.multiply(exchangeRateAmount);
//            // 返还的支付币种数量
//            BigDecimal priceDiffer = purchasedCopies.subtract(getCopiesByWeight).multiply(exchangeRateAmount);
            // 发币数量
            actualQuantityDistribute = getCopiesByWeight.multiply(amount);
//            // 扣除冻结资金
//            this.assetOfChange(userId, ieoOrder.getId(), ieoOrder.getPayCurrency(), payAmount, 77);
//            // 返还
//            this.assetOfChange(userId, ieoOrder.getId(), ieoOrder.getPayCurrency(), priceDiffer, 76);
//            // 发币
//            this.assetOfChange(userId, ieoOrder.getId(), ieoOrder.getCurrency(), actualQuantityDistribute, 78);

            // 订单处理完更新订单状态
            this.ieoOrderUpdate(ieoOrder, actualQuantityDistribute, payAmount, 1);
        }

    }

    private void ieoOrderUpdate(IeoOrder ieoOrder, BigDecimal actualQuantityDistribute, BigDecimal realPayAmount, Integer state) {
        // 得到数量
        ieoOrder.setAmount(actualQuantityDistribute);
        // 修正 得到数量(此字段优先级高于amount) 默认值为amount
        ieoOrder.setShareAmount(actualQuantityDistribute);
        // 实际支出数量
        ieoOrder.setRealPayAmount(realPayAmount);
        // 认购状态 0=待确认 1=成功 2=失败'
        ieoOrder.setState(state);
        // 活动状态 0=进行中 1=已结束
        ieoOrder.setActivityState(1);
        // 更新时间
        ieoOrder.setUpdateTime(new Date());
        ieoOrderMapper.updateByPrimaryKeySelective(ieoOrder);
    }

    /**
     * @param ieoOrder                 订单
     * @param userId                   用户id
     * @param actualQuantityDistribute 发币数量
     * @param now
     * @param price                    价格
     * @Description: 扣除资金和发放IEO打新币(非分期)
     * @Author: ChenXi
     * @Date: 2023/5/5 18:04
     * @return: void
     **/
    private void deductFrozenAndFundIssueCoin(IeoOrder ieoOrder, Long userId, BigDecimal actualQuantityDistribute,
                                              Date now, BigDecimal price) {
        // 订单id
        Long orderId = ieoOrder.getId();
        // 支付币种
        String payCurrency = ieoOrder.getPayCurrency();
        // 申购币种
        String currency = ieoOrder.getCurrency();

        // 扣除冻结资金
        ReturnResult<?> payReturnResult = ieoAssetOfChange(userId, orderId, payCurrency, ieoOrder.getRealPayAmount(),
                                                         Operation.IEO_PAY);

        // 扣除成功执行后面操作
        if(Objects.nonNull(payReturnResult) && payReturnResult.isSuccess()){
            // 返币
            BigDecimal priceDiffer = ieoOrder.getPayAmount().subtract(ieoOrder.getRealPayAmount());
            if(priceDiffer.compareTo(BigDecimal.ZERO) > 0){
                ieoAssetOfChange(userId, orderId, payCurrency, priceDiffer, Operation.IEO_UNFROZEN);
            }

            // 如果发币量为0, 不执行发币和生成发币记录
            if(actualQuantityDistribute.compareTo(BigDecimal.ZERO) > 0){
                // 发币
                ReturnResult<?> returnResult = ieoAssetOfChange(userId, orderId, currency, actualQuantityDistribute, Operation.IEO_INCOME, price);
                if (Objects.nonNull(returnResult) && returnResult.isSuccess()){
                    // 发放记录
                    ieoDistributionBuild(ieoOrder, userId, actualQuantityDistribute, now, currency, 0);
                }

            }
        }


    }

    /**
     * @Description: 分期发币(IEO5期 -> 新增分期, IEO6期 分期发币操作 1. 发放所有代币 -> 冻结 2.到分期解锁时间 冻结 -> 余额)
     * @Author: ChenXi
     * @Date:  2023/5/6 14:46
     * @param ieoOrder 订单
     * @param userId 用户id
     * @param actualQuantityDistribute 实际发币量
     * @param now now
     * @param number 期数
     * @param amount 发放百分比
     * @param flag 操作类型
     * @return: void
     **/
    private void deductFrozenAndFundIssueCoin(IeoOrder ieoOrder, Long userId, BigDecimal actualQuantityDistribute,
                                              Date now, Integer number, BigDecimal amount, boolean flag, BigDecimal price) {

        // 订单id
        Long orderId = ieoOrder.getId();
        // 支付币种
        String payCurrency = ieoOrder.getPayCurrency();
        // 申购币种
        String currency = ieoOrder.getCurrency();
        // 返还
        BigDecimal priceDiffer = BigDecimal.ZERO;
        if(flag){
            priceDiffer = ieoOrder.getPayAmount().subtract(ieoOrder.getRealPayAmount());
        }

        // 是否是扣币返币操作
        if(flag){
            // 扣除冻结资金  分期发币步骤 1. 点击发币时候扣除资金, 如有剩余则返还. 2. 将订单所有获得打新币增加到冻结中 3. 到达每期解锁时间时从冻结转到余额
            ReturnResult<?> payReturnResult = ieoAssetOfChange(userId, orderId, payCurrency, ieoOrder.getRealPayAmount(), Operation.IEO_PAY);
            if(Objects.nonNull(payReturnResult) && payReturnResult.isSuccess()){
                if(priceDiffer.compareTo(BigDecimal.ZERO) > 0){
                    ieoAssetOfChange(userId, orderId, payCurrency, priceDiffer, Operation.IEO_UNFROZEN);
                }
                // 分期发币 第1步. 把预发放的所有代币先增加到冻结中
                BigDecimal unfreeAmount = ieoOrder.getAmount();
                ieoAssetOfChange(userId, orderId, currency, unfreeAmount, Operation.IEO_FROZEN_INCOME, price);
            }


        } else {
            // 是否是发币操作
            // 发币量 = 发放百分比/100 * 发币量
            actualQuantityDistribute = amount.divide(new BigDecimal(100), 3, RoundingMode.DOWN).multiply(actualQuantityDistribute);

            // 如果发币量为0, 不执行发币和生成发币记录
            if(actualQuantityDistribute.compareTo(BigDecimal.ZERO) > 0){
                // 分期发币 2. 分期发放从冻结转到余额
                ReturnResult<?> returnResult = ieoAssetOfChange(userId, orderId, currency, actualQuantityDistribute, Operation.IEO_UNFROZEN_INCOME);
                if(Objects.nonNull(returnResult) && returnResult.isSuccess()){
                    // 发放记录
                    ieoDistributionBuild(ieoOrder, userId, actualQuantityDistribute, now, currency, number);
                }

            }
        }

    }

    /**
     * @Description:  生成发放记录
     * @Author: ChenXi
     * @Date:  2023/5/6 14:57
     * @param ieoOrder 订单
     * @param userId 用户id
     * @param actualQuantityDistribute 发放数量
     * @param now now
     * @param currency 打新币种
     * @param number 期数
     * @return: void
     **/
    private void ieoDistributionBuild(IeoOrder ieoOrder, Long userId, BigDecimal actualQuantityDistribute, Date now,
                                       String currency, Integer number) {

        IeoDistribution ieoDistribution = new IeoDistribution();
        // ieoId
        ieoDistribution.setIeoId(ieoOrder.getIeoId());
        // 项目名称
        ieoDistribution.setProjectName(ieoOrder.getProjectName());
        // orderId
        ieoDistribution.setOrderId(ieoOrder.getId());
        // 用户id
        ieoDistribution.setUserId(userId);
        // 打新币种
        ieoDistribution.setCurrency(currency);
        // 发币数量
        ieoDistribution.setAmount(actualQuantityDistribute);
        // 分期数
        ieoDistribution.setNumber(number);
        // 解锁时间
        ieoDistribution.setUnlockTime(now);
        ieoDistributionMapper.insertSelective(ieoDistribution);
    }

    /**
     * 黄涛
     * 支持者计数
     *
     * @param ieoId ieo id
     * @return {@link Integer}
     */
    public Integer supporterCount(Long ieoId) {
        return ieoOrderMapper.selectCountByIeoId(ieoId);
    }

    /**
     * 黄涛
     * 发现通过ieo id和副本
     *
     * @param userId 用户id
     * @return {@link Integer}
     */
    public Integer findSumCopiesByIeoId(Long userId) {
        Integer integer = ieoOrderMapper.selectSumCopiesByIeoId(userId);
        if (Objects.isNull(integer)) {
            return 0;
        }
        return integer;
    }

    /**
     * 黄涛
     * 某个项目售出币转计价币种的数量
     *
     * @param ieoId 项目id
     * @return usdt为单位币的数量
     */
    public IeoSubscribeCopyResponse  getOrder(Long ieoId) {

        Integer sumCopiesByIeoId = findSumCopiesByIeoId(ieoId);
        IeoCurrencyConfig ieoCurrencyConfig = ieoCurrencyConfigMapper.findByIsPricingCurrency(ieoId);
        BigDecimal amount = ieoCurrencyConfig.getAmount();

        IeoSubscribeCopyResponse ieoSubscribeCopyResponse = new IeoSubscribeCopyResponse();
        ieoSubscribeCopyResponse.setIeoId(ieoId);
        ieoSubscribeCopyResponse.setReal(amount.multiply(new BigDecimal(sumCopiesByIeoId)));
        ieoSubscribeCopyResponse.setCurrent(ieoCurrencyConfig.getCurrency());
        ieoSubscribeCopyResponse.setRealCopies(sumCopiesByIeoId);
        return  ieoSubscribeCopyResponse;
    }

    /**
     * @Description: 修改订单用户获得数量
     * @Author: ChenXi
     * @Date:  2023/3/22 18:43
     * @param language
     * @param ieoOrderList
     * @return: boolean
     **/
    public ReturnResult<?> modifyAmountByIds(String language, List<IeoOrderUpdateRequest> ieoOrderList) {
        int size = ieoOrderList.size();
        int flag = 0;

        if(size == 1){
            // 修改单个订单
            IeoOrderUpdateRequest ieoOrderUpdateRequest = ieoOrderList.get(0);
            if(Objects.isNull(ieoOrderUpdateRequest.getIeoId())){
                return ReturnResult.fail(getInternational(language, InternationalCode.LACK_VALUE));
            }

            // ieoId
            Long ieoId = ieoOrderUpdateRequest.getIeoId();
            // 订单ID
            Long orderId = ieoOrderUpdateRequest.getOrderId();

            IeoConfig ieoConfig = ieoConfigMapper.selectByPrimaryKey(ieoId);
            if(Objects.isNull(ieoConfig)){
                return ReturnResult.fail(getInternational(language, InternationalCode.NOT_FOUND));
            }

            // 已过解锁时间或者解冻过不能修改
            if(timeUnlock(ieoConfig)){
                return ReturnResult.fail(getInternational(language, InternationalCode.CHANG_UNLOCK));
            }

            // 预修改 得到数量
            BigDecimal requestAmount = ieoOrderUpdateRequest.getAmount();
            // 每份数量
            BigDecimal amount = ieoConfig.getAmount();
            // 分配总份数
            Integer sharePortion = ieoConfig.getSharePortion();

            // 排除此用户以外的已售出的份数和
            BigDecimal amountSum = ieoOrderMapper.getSumAmount(ieoId, orderId);
            if(Objects.isNull(amountSum)){
                amountSum = BigDecimal.ZERO;
            }
            IeoOrder currentIeoOrder = ieoOrderMapper.selectByPrimaryKey(orderId);

            if(Objects.isNull(currentIeoOrder)){
                return ReturnResult.fail(getInternational(language, InternationalCode.NOT_FOUND));
            }

            // 用户实际发放代币数量超过了每份数量限制
            if(exceedAmount(currentIeoOrder.getCopies(), amount, requestAmount)){
                return ReturnResult.fail(getInternational(language, InternationalCode.EXCEED_AMOUNT));
            }

            // 保存失败，该项目下所有用户实际得到代币数量总和超过了项目总发放数量
            if(exceedQuota(amountSum, requestAmount, sharePortion, amount)){
                return ReturnResult.fail(getInternational(language, InternationalCode.EXCEED_TOTAL));
            }

            // 计算修改之后的实际支出数量
            IeoCurrencyConfig ieoCurrencyConfig = ieoCurrencyConfigMapper.selectByIeoIdAndCurrency(ieoId, currentIeoOrder.getPayCurrency());
            if(Objects.isNull(ieoCurrencyConfig)){
                return ReturnResult.fail(getInternational(language, InternationalCode.NOT_FOUND));
            }
            // 修改后 需要支付的数量为  实际得到数量 / ieo配置每份数量 * 币种配置每份所需花费数量 (减少精度损失调整了先*后/)
            BigDecimal realPayAmount = requestAmount.multiply(ieoCurrencyConfig.getAmount())
                .divide(amount, 8, RoundingMode.UP);
            IeoOrder order = new IeoOrder();
            order.setId(orderId);
            order.setAmount(requestAmount);
            order.setRealPayAmount(realPayAmount);
            flag = ieoOrderMapper.updateByPrimaryKeySelective(order);

        } else if(size > 1){
            for (IeoOrderUpdateRequest ieoOrderUpdateRequest : ieoOrderList) {
                // ieoId
                Long ieoId = ieoOrderUpdateRequest.getIeoId();
                IeoConfig ieoConfig = ieoConfigMapper.selectByPrimaryKey(ieoId);
                if(Objects.isNull(ieoConfig)){
                    return ReturnResult.fail(getInternational(language, InternationalCode.NOT_FOUND));
                }

                // 已过解锁时间或者解冻过不能修改
                if(timeUnlock(ieoConfig)){
                    return ReturnResult.fail(getInternational(language, InternationalCode.CHANG_UNLOCK));
                }

                // 订单ID
                Long orderId = ieoOrderUpdateRequest.getOrderId();

                // 订单批量更改则为 异常用户获得数全改为0 真实支付数量为0
                IeoOrder order = new IeoOrder();
                order.setId(orderId);
                order.setRealPayAmount(BigDecimal.ZERO);
                order.setAmount(BigDecimal.ZERO);
                int i = ieoOrderMapper.updateByPrimaryKeySelective(order);

                if(i > 0){
                    flag++;
                }
            }
        }

        return flag > 0 ? ReturnResult.success(getInternational(language, InternationalCode.OPERATION_SUCCESS))
            : ReturnResult.fail(getInternational(language, InternationalCode.OPERATION_ERROR));
    }

    /**
     * @Description: 已经解冻过 或者 已过解锁时间
     * @Author: ChenXi
     * @Date:  2023/3/28 15:28
     * @param ieoConfig
     * @return: boolean
     **/
    private boolean timeUnlock(IeoConfig ieoConfig) {
        // 是否解冻并发放过新币 默认值为：0, 已结算:1 , 已发币:2
        Integer unfreeze = ieoConfig.getUnfreeze();
        return  unfreeze != 1;
    }

    /**
     * @Description: 判断修改之后的获得数量是否大于每份数量
     * @Author: ChenXi
     * @Date:  2023/3/23 10:28
     * @param copies 此次订单购买的份数
     * @param amount 此次IEO活动配置的每份数量
     * @param requestAmount 预修改的获得数量
     * @return: boolean
     **/
    private boolean exceedAmount(Integer copies, BigDecimal amount, BigDecimal requestAmount) {
        // 购买份数 * 每份数量 < 修改的获得数量
        return new BigDecimal(copies).multiply(amount).compareTo(requestAmount) < 0;
    }

    /**
     * @Description:  计算份数是否超额
     * @Author: ChenXi
     * @Date:  2023/3/22 20:04
     * @param amountSum  排除此订单id以外的所有订单, 获得数量总和
     * @param requestAmount  预修改的订单获得数量
     * @param sharePortion 此次活动分配的总份数
     * @param amount  活动配置每份数量
     * @return: boolean
     **/
    private boolean exceedQuota(BigDecimal amountSum, BigDecimal requestAmount,
                                Integer sharePortion, BigDecimal amount) {
        // 当前总分配数  < 修改后的所有订单 获得数量 之和
        return new BigDecimal(sharePortion).multiply(amount).compareTo(amountSum.add(requestAmount)) < 0;
    }

    @Transactional(rollbackFor = Exception.class)
    public void calculate(IeoConfig ieoConfig) {
        if(Objects.isNull(ieoConfig)){
            log.info("[IEO order calculate]  ieoConfig is null");
            return;
        }

        Long ieoId = ieoConfig.getId();
        Integer state = ieoConfig.getState();
        Integer unfreeze = ieoConfig.getUnfreeze();
        // 如果活动未开启, 所有订单已发币 则不处理
        if(state == 0 || unfreeze == 2){
            log.info("[IEO order calculate unfreeze !=0 ] 活动未开启或者已经发币, ieoId:{}, unfreeze:{}", ieoId, unfreeze);
            return;
        }

        // 判断活动时间 计算结束时间
        if(endTimeConfirm(ieoConfig.getEndTime())){
            log.info("[IEO order calculate endTime 当前结算时间now不得早于endTime (延后2分钟结算)] , ieoId:{}", ieoId);
            return;
        }

        // 所有订单
        List<IeoOrder> allIeoOrderList = ieoOrderMapper.selectOrderByIeoId(ieoId);
        if(allIeoOrderList.isEmpty()){
            log.info("[IEO order calculate] 订单为空, ieoId:{}", ieoId);
            return;
        }

        // 真实分配总份数
        Integer sharePortion = ieoConfig.getSharePortion();
        // 是否筹款成功
        if(sharePortion > 0){
            // 获取全部权重
            BigDecimal allUserWeight = allIeoOrderList.stream()
                .filter(Objects::nonNull)
                .map(s -> s.getWeight().multiply(new BigDecimal(s.getCopies())))
                .reduce(BigDecimal.ZERO, BigDecimal::add);
            // 存放总份数和总权重(随着每个订单处理逐步递减)
            Map<String,BigDecimal> remainingMap = new HashMap<>();
            remainingMap.put(COPIES_REMAINING, new BigDecimal(ieoConfig.getSharePortion()));
            remainingMap.put(WEIGHT_REMAINING, allUserWeight);
            // IEO打新币每份的数量
            BigDecimal amount = ieoConfig.getAmount();
            // 订单处理
            for (IeoOrder ieoOrder : allIeoOrderList) {
                this.orderProcessing(ieoOrder, remainingMap, amount);
            }
        } else {
            // 筹款失败
            for (IeoOrder ieoOrder : allIeoOrderList) {
                // 发放所有冻结货币
//                this.assetOfChange(ieoOrder.getUserId(), ieoOrder.getId(), ieoOrder.getPayCurrency(), ieoOrder.getPayAmount(), 76);
                // 订单处理
                this.ieoOrderUpdate(ieoOrder, BigDecimal.ZERO, BigDecimal.ZERO, 2);
            }
        }

        // 活动结束 更新活动状态
        // 状态 0=未开启 1=已结算 2=已发币
        ieoConfig.setUnfreeze(1);
        ieoConfigMapper.updateByPrimaryKeySelective(ieoConfig);
        log.info("[IEO order calculate] success, ieoId:{}", ieoId);
    }

    /**
     * @Description: 结束时间确认
     * @Author: ChenXi
     * @Date:  2023/3/23 17:49
     * @param endTime
     * @return: java.util.Date
     **/
    public boolean endTimeConfirm(Date endTime) {
        // end时间和now进行比对
        Date now = new Date();
        // endTime 在 now  之后
        if(endTime.after(now)){
            return true;
        } else {
            return false;
        }
    }

    /**
     * @Description: 发币
     * @Author: ChenXi
     * @Date:  2023/3/24 10:58
     * @param uri
     * @param ieoId ieoId
     * @return: void
     **/
    public ReturnResult<?> unfree(String uri, String language, Long ieoId) {
        IeoConfig ieoConfig = ieoConfigMapper.selectByPrimaryKey(ieoId);
        if(Objects.isNull(ieoConfig)){
            log.info("[IEO order unfree]  ieoConfig is null, ieoId:{}", ieoId);
            return ReturnResult.fail(getInternational(language, InternationalCode.NOT_FOUND));
        }
        Integer stage = ieoConfig.getStage();
        if(stage != 0){
            log.info("[IEO order stage != 0] 分期配置 error stage:{}", stage);
            return ReturnResult.fail(getInternational(language, InternationalCode.OPERATION_ERROR));
        }

        Integer state = ieoConfig.getState();
        Integer unfreeze = ieoConfig.getUnfreeze();
        // 如果活动未开启或者解冻过则不处理
        if(state == 0 || unfreeze != 1){
            log.info("[IEO order unfreeze != 1] 活动未开启或者已经解冻过, ieoId:{}, unfreeze:{}", ieoId, unfreeze);
            return ReturnResult.fail(getInternational(language, InternationalCode.NOT_OPEN_OR_THAWED));
        }

        // 判断活动时间 计算结束时间
        if(endTimeConfirm(ieoConfig.getEndTime())){
            log.info("[IEO order unfreeze endTime 当前结算时间now不得早于endTime (延后2分钟结算)] , ieoId:{}", ieoId);
            return ReturnResult.fail(getInternational(language, InternationalCode.ACTIVE_TIME_OR_STATUS));
        }

        // 所有订单
        List<IeoOrder> allIeoOrderList = ieoOrderMapper.selectOrderByIeoId(ieoId);
        if(allIeoOrderList.isEmpty()){
            log.info("[IEO order unfree] 订单为空, ieoId:{}", ieoId);
            return ReturnResult.fail(getInternational(language, InternationalCode.NO_ORDER));
        }

        //防止表单重复提交
        String repeatKey = repeatForm(uri, ieoId);
        if (!StringUtils.hasText(repeatKey)) {
            return new ReturnResult(StatusCode.ERROR, getInternational(language, InternationalCode.RESUBMIT));
        }

        Date now = new Date();

        //判断是否筹款成功 -> 真实分配总份数是否为0, 0则直接全部退款 大于0则成功
        Integer sharePortion = ieoConfig.getSharePortion();

        // 异步处理
        executorService.execute( () -> {

            if (sharePortion > 0) {
                // 筹款成功时, 发币情况下计算价格
                Map<String, BigDecimal> currencyPriceMap = getCurrencyPrice(ieoId, ieoConfig);

                for (IeoOrder ieoOrder : allIeoOrderList) {
                    try {
                        Long userId = ieoOrder.getUserId();
                        deductFrozenAndFundIssueCoin(ieoOrder, userId, ieoOrder.getAmount(), now, currencyPriceMap.get(ieoOrder.getPayCurrency()));
                    } catch (Exception e) {
                        log.error("筹款成功处理异常 - sharePortion:{}, ieoId:{}, ieoOrder:{}", sharePortion, ieoId, ieoOrder.getId(), e);
                    }
                }
            } else {
                for (IeoOrder ieoOrder : allIeoOrderList) {
                    try {
                        ieoAssetOfChange(ieoOrder.getUserId(), ieoOrder.getId(), ieoOrder.getPayCurrency(), ieoOrder.getPayAmount(), Operation.IEO_UNFROZEN);
                    } catch (Exception e) {
                        log.error("筹款失败处理异常 - sharePortion:{}, ieoId:{}, ieoOrder:{}", sharePortion, ieoId, ieoOrder.getId(), e);
                    }
                }
            }

        });

        // 更新ieo配置状态
        IeoConfig unfreeIeoConfig = new IeoConfig();
        unfreeIeoConfig.setId(ieoId);
        unfreeIeoConfig.setUnfreeze(2);
        unfreeIeoConfig.setUnlockTime(now);
        ieoConfigMapper.updateByPrimaryKeySelective(unfreeIeoConfig);
        log.info("[IEO order unfree success], ieoId:{}", ieoId);

        //删除redis标记
        RedisUtil.delete(repeatKey);
        return ReturnResult.success(getInternational(language, InternationalCode.OPERATION_SUCCESS));
    }



    /**
     * @Description: 扣除,返币和发币操作
     * @Author: ChenXi
     * @Date:  2023/5/9 10:36
     * @param ieoStages 分期配置
     * @param allIeoOrderList 订单
     * @param now now
     * @param flag true = 扣币, 返还, false = 发币
     * @return: void
     **/
    public void stagesUnfree(IeoStages ieoStages, List<IeoOrder> allIeoOrderList, Date now, boolean flag) {

        // 期数
        Integer number = ieoStages.getNumber();

        // 发放百分比
        BigDecimal amount = ieoStages.getAmount();

        // 订单处理
        for (IeoOrder ieoOrder : allIeoOrderList) {
            try {
                Long userId = ieoOrder.getUserId();
                // 发币
                deductFrozenAndFundIssueCoin(ieoOrder, userId, ieoOrder.getAmount(), now, number, amount ,flag, null);
            } catch (Exception e) {
                log.error("ieoStageDistribution stagesUnfree ieoId:{}, number:{}, ieoOrderId:{}, flag:{}", ieoStages.getIeoId(), number, ieoOrder.getId() ,flag);
            }
        }
    }

    /**
     * @Description: 根据IeoId查询所有order
     * @Author: ChenXi
     * @Date:  2023/5/6 15:52
     * @param ieoId
     * @return: java.util.List<com.novax.ex.activity.infrastructure.entity.IeoOrder>
     **/
    public List<IeoOrder> selectOrderByIeoId(Long ieoId) {
        return ieoOrderMapper.selectOrderByIeoId(ieoId);
    }

    /**
     * @Description: 分期发币操作
     * @Author: ChenXi
     * @Date:  2023/5/8 17:19
     * @param uri
     * @param language
     * @param ieoId
     * @return: com.novax.ex.common.results.ReturnResult<?>
     **/
    public ReturnResult<?> stages(String uri, String language, Long ieoId) {
        // now
        Date now = new Date();

        IeoConfig ieoConfig = ieoConfigMapper.selectByPrimaryKey(ieoId);
        if(Objects.isNull(ieoConfig)){
            log.info("[IEO order stages unfree]  ieoConfig is null, ieoId:{}", ieoId);
            return ReturnResult.fail(getInternational(language, InternationalCode.NOT_FOUND));
        }

        Integer stage = ieoConfig.getStage();
        if(stage != 1){
            log.info("ieoOrderService stages  ieoConfig error stage:{}", stage);
            return ReturnResult.fail(getInternational(language, InternationalCode.OPERATION_ERROR));
        }

        Integer state = ieoConfig.getState();
        Integer unfreeze = ieoConfig.getUnfreeze();
        // 如果活动未开启或者解冻过则不处理
        if(state == 0 || unfreeze != 1){
            log.info("[IEO order stages unfreeze != 1] 活动未开启或者已经解冻过, ieoId:{}, unfreeze:{}", ieoId, unfreeze);
            return ReturnResult.fail(getInternational(language, InternationalCode.NOT_OPEN_OR_THAWED));
        }

        // 判断活动时间 计算结束时间
        if(endTimeConfirm(ieoConfig.getEndTime())){
            log.info("[IEO order stages unfreeze endTime 当前结算时间now不得早于endTime (延后2分钟结算)] , ieoId:{}", ieoId);
            return ReturnResult.fail(getInternational(language, InternationalCode.ACTIVE_TIME_OR_STATUS));
        }

        // 所有订单
        List<IeoOrder> allIeoOrderList = ieoOrderMapper.selectOrderByIeoId(ieoId);
        if(allIeoOrderList.isEmpty()){
            log.info("[IEO order stages unfree] 订单为空, ieoId:{}", ieoId);
            return ReturnResult.fail(getInternational(language, InternationalCode.NO_ORDER));
        }

        //防止表单重复提交
        String repeatKey = repeatForm(uri, ieoId);
        if (!StringUtils.hasText(repeatKey)) {
            return new ReturnResult(StatusCode.ERROR, getInternational(language, InternationalCode.RESUBMIT));
        }


        //判断是否筹款成功 -> 真实分配总份数是否为0, 0则直接全部退款 大于0则成功
        Integer sharePortion = ieoConfig.getSharePortion();

        executorService.execute(() -> {

            if (sharePortion > 0) {

                // 筹款成功时, 发币情况下计算价格
                Map<String, BigDecimal> currencyPriceMap = getCurrencyPrice(ieoId, ieoConfig);
                for (IeoOrder ieoOrder : allIeoOrderList) {
                    try {
                        Long userId = ieoOrder.getUserId();
                        // 手动点击发币按钮 执行扣币 返还操作
                        deductFrozenAndFundIssueCoin(ieoOrder, userId, ieoOrder.getAmount(), now, null, null, true, currencyPriceMap.get(ieoOrder.getPayCurrency()));
                    } catch (Exception e) {
                        log.error("ieoOrderService stages deductFrozenAndFundIssueCoin sharePortion:{},ieoId:{}, ieoOrderId:{}", sharePortion, ieoId, ieoOrder.getId());
                    }
                }
            } else {
                for (IeoOrder ieoOrder : allIeoOrderList) {
                    try {
                        // 返还
                        ieoAssetOfChange(ieoOrder.getUserId(), ieoOrder.getId(), ieoOrder.getPayCurrency(), ieoOrder.getPayAmount(), Operation.IEO_UNFROZEN);
                    } catch (Exception e) {
                        log.error("ieoOrderService stages ieoAssetOfChange sharePortion:{},ieoId:{}, ieoOrderId:{}", sharePortion, ieoId, ieoOrder.getId());
                    }
                }
            }

        });

        // 更新ieo配置状态
        IeoConfig unfreeIeoConfig = new IeoConfig();
        unfreeIeoConfig.setId(ieoId);
        unfreeIeoConfig.setUnfreeze(3);
        ieoConfigMapper.updateByPrimaryKeySelective(unfreeIeoConfig);
        log.info("[IEO order stages unfree success], ieoId:{}, sharePortion:{}", ieoId, sharePortion);

        //删除redis标记
        RedisUtil.delete(repeatKey);
        return ReturnResult.success(getInternational(language, InternationalCode.OPERATION_SUCCESS));
    }

    /**
     * @Description:  针对分期的某期 补偿发币
     * @Author: ChenXi
     * @Date:  2023/5/9 15:37
     * @param uri
     * @param language
     * @param id
     * @return: com.novax.ex.common.results.ReturnResult<?>
     **/
    @Transactional(rollbackFor = Exception.class)
    public ReturnResult<?> reissue(String uri, String language, Long id) {
        IeoStages ieoStages = ieoStagesMapper.selectByPrimaryKey(id);
        if(Objects.isNull(ieoStages)){
            log.info("ieoOrderService reissue ieoStages isNull, stagesId:{}", id);
            return ReturnResult.fail(getInternational(language, InternationalCode.NOT_FOUND));
        }

        Date unlockTime = ieoStages.getUnlockTime();
        // 判断活动时间 计算结束时间
        if(endTimeConfirm(unlockTime)){
            log.info("ieoOrderService reissue 当前结算时间now不得早于unlockTime  分期配置 id:{}", id);
            return ReturnResult.fail(getInternational(language, InternationalCode.ACTIVE_TIME_OR_STATUS));
        }

        // 判断是否发过币
        Integer state = ieoStages.getState();
        if(state.equals(1)){
            log.info("ieoOrderService reissue 已发币 stagesId:{}, state:{}", id, state);
            return ReturnResult.fail(getInternational(language, InternationalCode.NOT_OPEN_OR_THAWED));
        }

        // 获取ieoId
        Long ieoId = ieoStages.getIeoId();

        IeoConfig ieoConfig = ieoConfigMapper.selectByPrimaryKey(ieoId);
        if(Objects.isNull(ieoConfig)){
            log.info("ieoOrderService reissue ieoConfig isNull, ieoId:{}", ieoId);
            return new ReturnResult(StatusCode.ERROR, getInternational(language, InternationalCode.NOT_FOUND));
        }

        Integer stage = ieoConfig.getStage();
        if(stage != 1){
            log.info("ieoOrderService reissue  ieoConfig error stage:{}", stage);
            return ReturnResult.fail(getInternational(language, InternationalCode.OPERATION_ERROR));
        }
        Integer ieoConfigState = ieoConfig.getState();
        Integer unfreeze = ieoConfig.getUnfreeze();
        if(ieoConfigState != 1 || unfreeze < 3){
            log.info("ieoOrderService reissue ieoConfig ieoConfigState:{}, unfreeze:{}", ieoConfigState, unfreeze);
            return ReturnResult.fail(getInternational(language, InternationalCode.NOT_OPEN_OR_THAWED));
        }
        // 所有订单
        List<IeoOrder> allIeoOrderList = ieoOrderMapper.selectOrderByIeoId(ieoId);
        if(allIeoOrderList.isEmpty()){
            log.info("ieoOrderService reissue 订单为空, ieoId:{}", ieoId);
            return ReturnResult.fail(getInternational(language, InternationalCode.NO_ORDER));
        }

        //防止表单重复提交
        String repeatKey = repeatForm(uri, id);
        if (!StringUtils.hasText(repeatKey)) {
            log.info("ieoOrderService reissue fail -> 重复提交 ");
            return new ReturnResult(StatusCode.ERROR, getInternational(language, InternationalCode.RESUBMIT));
        }

        // 发币数
        Integer sharePortion = ieoConfig.getSharePortion();

        Date now = new Date();
        if(sharePortion > 0){
            executorService.execute(() ->{
                stagesUnfree(ieoStages, allIeoOrderList, now, false);
            });

        } else {
            log.info("ieoOrderService reissue 分配分数为0, sharePortion:{}", sharePortion);
            //删除redis标记
            RedisUtil.delete(repeatKey);
            return ReturnResult.fail(getInternational(language, InternationalCode.OPERATION_ERROR));
        }

        // 更新状态
        ieoStages.setState(1);
        ieoStagesMapper.updateByPrimaryKeySelective(ieoStages);

        // 如果处理了最后一期
        // 最后一期ID
        Long lastIdByIeoId = ieoStagesMapper.findLastIdByIeoId(ieoId);
        // 是否最后一期
        if(lastIdByIeoId.equals(id)){
            ieoConfig.setUnfreeze(4);
            ieoConfigMapper.updateByPrimaryKeySelective(ieoConfig);
        }

        //删除redis标记
        RedisUtil.delete(repeatKey);
        return ReturnResult.success(getInternational(language, InternationalCode.OPERATION_SUCCESS));

    }


    /**
     * @param ieoId
     * @param ieoConfig
     * @Description: 在发币情况下计算出价格
     * @Author: ChenXi
     * @Date: 2024/3/26 15:23
     * @return: void
     **/
    private Map<String, BigDecimal> getCurrencyPrice(Long ieoId, IeoConfig ieoConfig) {
        // 存放币种价格
        Map<String, BigDecimal> currencyPriceMap = new HashMap<>();

        // 查询此IEO币种配置
        List<IeoCurrencyConfig> ieoCurrencyConfigs = ieoCurrencyConfigMapper.selectByIeoId(ieoId);
        if (!ieoCurrencyConfigs.isEmpty()) {
            for (IeoCurrencyConfig ieoCurrencyConfig : ieoCurrencyConfigs) {
                // 购买打新币一份需要的数量
                BigDecimal amount = ieoCurrencyConfig.getAmount();
                // 一份打新币多少个
                BigDecimal amountOfCop = ieoConfig.getAmount();
                if (Currency.USDT.equals(ieoCurrencyConfig.getCurrency())) {
                    currencyPriceMap.put(ieoCurrencyConfig.getCurrency(), amount.divide(amountOfCop, 8, RoundingMode.DOWN));
                } else {
                    currencyPriceMap.put(ieoCurrencyConfig.getCurrency(), ieoCurrencyConfig.getUsdtRate().
                        multiply(amount).
                        divide(amountOfCop, 8, RoundingMode.DOWN).
                        setScale(8, RoundingMode.DOWN));
                }

            }

        }

        return currencyPriceMap;
    }

}
